package com.javacoo.xservice.base;

import java.util.Optional;
import java.util.function.Function;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.RequestMapping;

import com.javacoo.xservice.base.bean.BaseParameter;
import com.javacoo.xservice.base.config.Resources;
import com.javacoo.xservice.base.exception.IllegalParameterException;
import com.javacoo.xservice.base.support.swaparea.SwapAreaUtils;
import com.javacoo.xservice.base.utils.FastJsonUtil;
import com.javacoo.xservice.base.utils.WebUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 业务参数控制器基类
 * <p>说明:</p>
 * <li>定义有业务参数的接口处理基本流程</li>
 * @author DuanYong
 * @param <P> 参数
 * @since 2017年6月28日下午2:48:27
 */
@Slf4j
public abstract class AbstractParamController<P extends BaseParameter> extends BaseController {
	/**
	 * 接口处理
	 * <p>说明:</p>
	 * <li>1:请求参数解析</li>
	 * <li>2:检查请求参数</li>
	 * <li>3:业务处理</li>
	 * <li>4:设置响应数据</li>
	 * @author DuanYong
	 * @since 2017年6月28日下午3:19:43
     * @param response 响应对象
	 */
	@RequestMapping
	public final void handle(HttpServletResponse response) {
		final Long startTime = System.currentTimeMillis();
        //参数解析->检查请求参数->业务处理->设置响应数据
		parse().map(r->validateFunction.apply(r)).map(r->Optional.ofNullable(execute(r))).map(o->setSuccessResponse(response,o.orElse(Optional.empty())));
        log.info("[{}]接口->{},处理完成,耗时->{}秒",SwapAreaUtils.getSwapAreaData().getTransactionSn(), SwapAreaUtils.getSwapAreaData().getReqMethod(),(System.currentTimeMillis() - startTime)/1000.0);
	}
	/**
	 * 执行
	 * <p>说明:</p>
	 * <li>hystrix</li>
	 * @author DuanYong
     * @since 2017年11月13日下午3:41:04
	 * @param p 业务参数
     * @return: java.lang.Object 业务返回对象
	 */
	private final Object execute(P p){
		return executeFunction.apply(p);
	}
	/**
	 * 解析请求参数
	 * <p>说明:</p>
	 * <li>将请求参数中的业务参数对象转换为服务使用的对象</li>
	 * @author DuanYong
	 * @since 2017年6月28日下午3:17:32
     * @return: java.util.Optional<P> 业务参数对象
	 */
	protected final Optional<P> parse(){
		BaseRequest baseRequest = SwapAreaUtils.getSwapAreaData().getBaseRequest();
		baseRequest.getParameter().orElseThrow(()->new IllegalParameterException(Resources.getMessage(ErrorCodeConstants.COMMON_REQ_PARAM_PARSE_IS_EMPTY)));
		try{
			return baseRequest.getParameter().map(o->o.toString()).map(s->initBaseParameter(s,baseRequest));
		}catch(Exception ex){
			ex.printStackTrace();
            log.error("将请求参数中的业务参数对象转换为服务使用的对象失败,流水号:{},请求参数:{},异常信息:", WebUtil.getSwapAreaData().getTransactionSn(),baseRequest.getParameter(),ex);
			throw new IllegalParameterException(Resources.getMessage(ErrorCodeConstants.COMMON_REQ_PARAM_PARSE_ERROR));
		}
	}
	/**
	 * 初始化初始请求参数
	 * <p>说明:</p>
	 * <li>解析并初始化请求参数对象</li>
	 * @author DuanYong
	 * @param paramString 参数原始json字符串
     * @param baseRequest 请求参数对象
	 * @return P 业务参数对象
	 * @since 2017年11月14日上午11:07:19
	 */
	private P initBaseParameter(String paramString, BaseRequest baseRequest){
		P p = FastJsonUtil.toBean(paramString,getParamClass());
		p.setChannelNo(baseRequest.getAppKey());
		p.setTransactionSn(baseRequest.getTransactionSn());
		p.setQueryStringMap(baseRequest.getQueryStringMap());
		return p;
	}
	/**
	 * 校验请求中的业务参数
	 * <p>说明:</p>
	 * <li>由子类实现，如果参数检查不通过，请抛出参数异常:IllegalParameterException</li>
	 * @author DuanYong
	 * @param p 业务参数对象
	 * @throws IllegalParameterException
	 * @since 2017年6月28日下午2:28:10
	 */
	protected abstract void validate(P p) throws IllegalParameterException;
	/**
	 * 具体业务处理
	 * <p>说明:</p>
	 * <li>由子类实现</li>
	 * @author DuanYong
	 * @param p 业务参数对象
	 * @return 业务返回数据
	 * @since 2017年5月5日下午3:24:09
	 */
	protected abstract Object process(P p);
	/**
	 * 获取参数类型
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @return 参数类型对象
	 * @since 2017年7月24日上午10:33:30
	 */
	protected abstract Class<P> getParamClass();
	/**
	 * 服务降级，默认返回REQUEST_TIMEOUT字符串，框架统一处理抛出TimeoutException异常
	 * <p>说明:</p>
	 * <li>注意：在fallback方法中不允许有远程方法调用，方法尽量要轻，调用其他外部接口也要进行hystrix降级。否则执行fallback方法会抛出异常</li>
	 * @author DuanYong
	 * @param p 参数
	 * @return REQUEST_TIMEOUT
	 * @since 2018年8月21日上午11:20:37
	 */
	protected Object fallback(P p){
		return Constants.REQUEST_TIMEOUT;
	}

	/**
	 * 校验并返回业务参数
	 */
	private Function<P,P> validateFunction = (P p)->{
		validate(p);
		return p;
	};
	/**
	 * 执行业务处理
	 */
	private Function<P,Object> executeFunction = (P p)-> process(p);
	/**
	 * 执行降级业务处理
	 */
	private Function<P,Object> fallbackFunction = (P p)-> fallback(p);
}
